// Ticket Taker Agent
//
// This function represents the logic and activities of a ticket taker in the movie theater simulation.
// This function will be forked onto a thread during initialization.
//

#include "TicketTakerAgent.h"
#include "globalsExtern.cc"
#include "utility.h"

void TicketTakerAgentExecute(int arg) {
	TicketStub* stub;
	int groupSize = 0;
	int theaterID = 0;
	int TicketTakerID = arg;

	// Ticket Taker Agent running code should go here.
	DEBUG('A', "%s: Thread now running.\n", currentThread->getName());
	
	
	
	ticketTakerCustomerLock[TicketTakerID]->Acquire();
	ticketTakerCondition[TicketTakerID]->Wait(ticketTakerCustomerLock[TicketTakerID]);
	// Loop Invariant - Finished with last customer, starting a new conversation.
	while (true) {
	   // Wake up the next customer in our line and tell him to approach.
	   takerLineSelectionLock->Acquire();
	   takerLineSelectionCondition[TicketTakerID]->Signal(takerLineSelectionLock);
	   takerLineSelectionLock->Release();
	   ticketTakerCondition[TicketTakerID]->Wait(ticketTakerCustomerLock[TicketTakerID]);

	   // At this point the customer is waiting at our kiosk and has
	   // put their ticket stub on the whiteboard
	   theaterSelectionLock->Acquire();
	   
	   // Make sure there are enough available seats in the theater before letting in 
	   // the customers
	   groupSize = ticketTakerCustomerWhiteboard[TicketTakerID]->getNumTickets();
	   DEBUG('R',"%s has received %d tickets.\n", currentThread->getName(),groupSize);
	   while (theaterSelectionWhiteboard->getSeatsAvail() < groupSize) {
		  DEBUG('R',"%s is not allowing the group into the theater. The number of taken tickets is %d and the group size is %d.\n", currentThread->getName(),25-theaterSelectionWhiteboard->getSeatsAvail(),groupSize);
		  // Inform the manager that the theater was full when we tried to let in a group and he should consider starting the show.
		  DEBUG('A', "%s: Letting the manager my next group does not fit in the theater and he should consider starting the movie.\n", currentThread->getName());
		  managerLock->Acquire();
		  managerWhiteboard->setTakerClerkTheaterStatus(TicketTakerID, managerWhiteboard->TAKER_CLERK_STOPPED);
		  managerCondition->Signal(managerLock);
		  managerLock->Release();
		  DEBUG('R',"%s has stopped taking tickets.\n", currentThread->getName());
		  theaterTicketTakerCondition->Wait(theaterSelectionLock);
		  DEBUG('T',"%s: Woken by the movie Tech. The number of taken tickets is %d.\n", currentThread->getName(),25-theaterSelectionWhiteboard->getSeatsAvail());
	   }
	   
	   // Ok to let the group into the theater, decrement seats available in the theater
	   theaterSelectionWhiteboard->decrementSeatsAvail(groupSize);
	   // obtain the theater ID from the ticket stub to tell the customers where to go 
	   DEBUG('R', "%s is allowing the group into the theater. The number of tickets taken is %d.\n",currentThread->getName(), groupSize);
	   DEBUG('R', "%s: number of available seats left in theater is %d.\n",currentThread->getName(),theaterSelectionWhiteboard->getSeatsAvail());
	   theaterSelectionLock->Release();
	   
	   DEBUG('R',"%s has a line length %d and is signaling a customer.\n", currentThread->getName(),takerLineSelectionWhiteboard->getLineLength(TicketTakerID));
	   takerCustomerCondition[TicketTakerID]->Signal(ticketTakerCustomerLock[TicketTakerID]);
	   ticketTakerCondition[TicketTakerID]->Wait(ticketTakerCustomerLock[TicketTakerID]);
		
		takerLineSelectionLock->Acquire();
	    
		DEBUG('D', "Line length is %d\n", takerLineSelectionWhiteboard->getLineLength(TicketTakerID));
	   if (takerLineSelectionWhiteboard->getLineLength(TicketTakerID) == 0) {
		  DEBUG('R', "%s has no one in line. I am available for a customer.\n", currentThread->getName(), takerLineSelectionWhiteboard->getLineLength(TicketTakerID));
		  // Let the manager know that our line is empty so he should consider starting the movie.
		  takerLineSelectionLock->Release();
		  DEBUG('A', "%s: Letting the manager know my line is empty and he should consider starting the movie.\n", currentThread->getName());
		  managerLock->Acquire();
		  //DEBUG('A', "%s: Acquired the manager lock.\n", currentThread->getName());
		  managerWhiteboard->setTakerClerkTheaterStatus(TicketTakerID, managerWhiteboard->TAKER_CLERK_STOPPED);
		  managerCondition->Signal(managerLock);
		  //DEBUG('A', "%s: Signaled the manager, releasing the manager lock.\n", currentThread->getName());
		  managerLock->Release();
		  // There is no one in our line, sleeping
		  ticketTakerCondition[TicketTakerID]->Wait(ticketTakerCustomerLock[TicketTakerID]);
	   }
	   else {
		  DEBUG('A', "%s: Calling next customer to my window.\n", currentThread->getName());
		  takerLineSelectionLock->Release();
	   }
	   
	}
}

void ClearTicketTakerCustomerWhiteboard(int TicketTakerID) {
    DEBUG('A', "%s: Clearing whiteboard.\n", currentThread->getName());
}
